/**
 * @file monitor.c
 * @brief Source file to compile monitor.so, containing all methods for monitor.
 * @author Hongbao Chen
 * @version 1.0 beta
 * @date 2012-04-11
 */

#ifndef MACHINFO_H_
#define MACHINFO_H_

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ifaddrs.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#ifndef SSTRING_H_
#include "sstring.h"
#endif
#ifndef STEXT_H_
#include "stext.h"
#endif
#ifndef SXML_H_
#include "sxml.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#define CPU_INFO	"/proc/cpuinfo"
#define MEM_INFO	"/proc/meminfo"
#define SYS_LOAD	"/proc/stat"
#define NET_LOAD	"/proc/net/dev"
#define KNL_HOST	"/proc/sys/kernel/hostname"
#define KNL_VERS	"/proc/sys/kernel/version"


#ifndef MAX_BUF_SIZE

#define MAX_BUF_SIZE	1024
#endif

int getMachineInfo(char *_pProc, struct sstring *_pStr) {
	if(_pStr == (String *)NULL) {
		return (int)-1;
	}
	if( !api_isFileExist(_pProc)) {
		return (int)-1;
	}
	
	int n = 0, total = 0;
	char buf[MAX_BUF_SIZE+1];
	FILE *pFile = fopen( _pProc, "r");
	if(pFile != (FILE *)NULL) {
		while((n = fread(buf, sizeof(char), MAX_BUF_SIZE, pFile)) > 0) {
			total += n;
			buf[n] = '\0';
			sstrCat(_pStr, buf);
		}
	} else {
		snprintf(buf, MAX_BUF_SIZE-1, "Unable to read from file: %s.", _pProc);
		API_ERR_LOG(buf);
		return (int)-1;
	}

	if( total < 1) {
		snprintf(buf, MAX_BUF_SIZE-1, "Nothing read from file: %s.", _pProc);
		API_ERR_LOG(buf);
		return (int)-1;
	}
	return (int)0;
}


#define HWADDR_len 6

#define MAX_IF_NUM	8
#define MAX_IP_LEN	INET6_ADDRSTRLEN
#define MAX_MAC_LEN	14
#define MAX_IF_NAME	16

struct AddressPack {
	char names	[MAX_IF_NUM][MAX_IF_NAME];
	char ips	[MAX_IF_NUM][MAX_IP_LEN];
	char macs	[MAX_IF_NUM][MAX_MAC_LEN];
	char vers	[MAX_IF_NUM];
	int count;
};

int getLocalAddr(struct AddressPack *_pAddrPack) {

	struct ifaddrs * ifAddrStruct=NULL;
    	struct ifaddrs * ifa=NULL;
        void * tmpAddrPtr=NULL;

	struct ifreq ifr;
	int s,i;

	getifaddrs(&ifAddrStruct);
	s = socket(AF_INET, SOCK_DGRAM, 0);

	for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
		if (ifa ->ifa_addr->sa_family==AF_INET && strcmp(ifa->ifa_name, "lo")!=0) { 
	        	tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
		        inet_ntop(AF_INET, tmpAddrPtr, _pAddrPack->ips[ _pAddrPack->count], INET_ADDRSTRLEN);
			
		        strcpy(ifr.ifr_name, ifa->ifa_name);
		    	ioctl(s, SIOCGIFHWADDR, &ifr);
		        for (i=0; i<HWADDR_len; i++)
			        sprintf(&( _pAddrPack->macs[ _pAddrPack->count][i*2]),"%02X",((unsigned char*)ifr.ifr_hwaddr.sa_data)[i]);
		    	_pAddrPack->macs[ _pAddrPack->count][12]='\0';

			_pAddrPack->vers[ _pAddrPack->count] = '4';
			memcpy(_pAddrPack->names[ _pAddrPack->count], ifa->ifa_name, strlen(ifa->ifa_name)+1);
			
			++_pAddrPack->count;
        	} else if(ifa->ifa_addr->sa_family==AF_INET6 && strcmp(ifa->ifa_name, "lo")!=0) {

	        	tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
		        inet_ntop(AF_INET6, tmpAddrPtr, _pAddrPack->ips[ _pAddrPack->count], INET6_ADDRSTRLEN);
			
		        strcpy(ifr.ifr_name, ifa->ifa_name);
		    	ioctl(s, SIOCGIFHWADDR, &ifr);
		        for (i=0; i<HWADDR_len; i++)
			        sprintf(&( _pAddrPack->macs[ _pAddrPack->count][i*2]),"%02X",((unsigned char*)ifr.ifr_hwaddr.sa_data)[i]);
		    	_pAddrPack->macs[ _pAddrPack->count][12]='\0';

			_pAddrPack->vers[ _pAddrPack->count] = '6';
			memcpy(_pAddrPack->names[ _pAddrPack->count], ifa->ifa_name, strlen(ifa->ifa_name)+1);
			
			++_pAddrPack->count;
		}

    	}

	close(s);
    	if (ifAddrStruct!=NULL) 
		freeifaddrs(ifAddrStruct);

        return 0;
}

static char *pXmltAttr	= "\t\t<attribute id=\"@id@\">\r\n\t\t\t<name>@name@</name>\r\n\t\t\t<value>@value@</value>\r\n\t\t</attribute>\r\n";
static char *pXmltCpu	= "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n\
<cpuinfo>\r\n\
\t<meta>\r\n\
\t\t<hostname>@hostname@</hostname>\r\n\
\t\t<os>@os@</os>\r\n\
\t\t<timestamp>@timestamp@</timestamp>\r\n\
\t</meta>\r\n\
\t<content>\r\n\
@attr@\r\n\
\t</content>\r\n\
</cpuinfo>\r\n";

char * getCpuXml(struct XML *_pXml) {

	char buf[MAX_BUF_SIZE];
	bzero(buf, MAX_BUF_SIZE);
	struct XmlFraction *pAttrFrac	= NULL;
	struct XmlFraction *pCpuFrac	= NULL;

	if((pCpuFrac = buildXmlFraction(pXmltCpu, pCpuFrac)) == NULL) {
		return (char *)NULL;
	}

	//Generate XML here.
	String str = SSTR_INIT;

	if(getMachineInfo(CPU_INFO, &str) != 0) {
		return NULL;
	}
	struct KVPair *pkv = NULL;
	struct XML xml;
	bzero(&xml, sizeof(struct XML));
	pkv = api_textToKV(str.phead, " \t\r:");

	int id=0;

	for( ; pkv!=NULL; pkv=pkv->pnext) {
		if((pAttrFrac = buildXmlFraction(pXmltAttr, NULL)) == NULL) {
			return (char *)NULL;
		}
		++id;
		snprintf(buf, MAX_BUF_SIZE, "%d", id);
		setXmlValue("id", buf, pAttrFrac);
		setXmlValue("name", pkv->akey, pAttrFrac);
		setXmlValue("value", pkv->avalue, pAttrFrac);
		if(realizeXmlFraction(&xml, pAttrFrac) == NULL ) {
			return (char *)NULL;
		}
		setXmlValue("attr", xml.xml_string.phead, pCpuFrac);

		freeXmlFraction(pAttrFrac);

	}

	sstrClear(&str);
	if(getMachineInfo(KNL_HOST, &str) != 0) {
		return (char *)NULL;
	}
	api_stripSpace(str.phead);
	setXmlValue("hostname", str.phead, pCpuFrac);

	sstrClear(&str);
	if(getMachineInfo(KNL_VERS, &str) != 0) {
		return (char *)NULL;
	}
	api_stripSpace(str.phead);
	setXmlValue("os", str.phead, pCpuFrac);
	time_t t=time(0);
	setXmlValue("timestamp", api_stripSpace(asctime(localtime(&t))), pCpuFrac);

	if( _pXml!= NULL && realizeXmlFraction(_pXml, pCpuFrac) == NULL) {
		return (char *)NULL;
	} else {
		if(realizeXmlFraction(&xml, pCpuFrac) == NULL)
			return (char *)NULL;
	}

	char *pRet = NULL;
	if( _pXml==NULL) {
		pRet = xml.xml_string.phead;
	} else {
		pRet = _pXml->xml_string.phead;
	}

	freeXmlFraction(pCpuFrac);

	return pRet;

}



static char *pXmltMem	= "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n\
<meminfo>\r\n\
\t<meta>\r\n\
\t\t<hostname>@hostname@</hostname>\r\n\
\t\t<os>@os@</os>\r\n\
\t\t<timestamp>@timestamp@</timestamp>\r\n\
\t</meta>\r\n\
\t<content>\r\n\
@attr@\r\n\
\t</content>\r\n\
</meminfo>\r\n";

char * getMemXml(struct XML *_pXml) {

	char buf[MAX_BUF_SIZE];
	bzero(buf, MAX_BUF_SIZE);
	struct XmlFraction *pAttrFrac	= NULL;
	struct XmlFraction *pMemFrac	= NULL;

	if((pMemFrac = buildXmlFraction(pXmltMem, pMemFrac)) == NULL) {
		return (char *)NULL;
	}

	//Generate XML here.
	String str = SSTR_INIT;

	if(getMachineInfo(MEM_INFO, &str) != 0) {
		return NULL;
	}
	struct KVPair *pkv = NULL;
	struct XML xml;
	bzero(&xml, sizeof(struct XML));
	pkv = api_textToKV(str.phead, " \t\r:");

	int id=0;

	for( ; pkv!=NULL; pkv=pkv->pnext) {
		if((pAttrFrac = buildXmlFraction(pXmltAttr, NULL)) == NULL) {
			return (char *)NULL;
		}
		++id;
		snprintf(buf, MAX_BUF_SIZE, "%d", id);
		setXmlValue("id", buf, pAttrFrac);
		setXmlValue("name", pkv->akey, pAttrFrac);
		setXmlValue("value", pkv->avalue, pAttrFrac);
		if(realizeXmlFraction(&xml, pAttrFrac) == NULL ) {
			return (char *)NULL;
		}
		setXmlValue("attr", xml.xml_string.phead, pMemFrac);

		freeXmlFraction(pAttrFrac);

	}


	sstrClear(&str);
	if(getMachineInfo(KNL_HOST, &str) != 0) {
		return (char *)NULL;
	}
	api_stripSpace(str.phead);
	setXmlValue("hostname", str.phead, pMemFrac);

	sstrClear(&str);
	if(getMachineInfo(KNL_VERS, &str) != 0) {
		return (char *)NULL;
	}	
	api_stripSpace(str.phead);
	setXmlValue("os", str.phead, pMemFrac);
	time_t t=time(0);
	setXmlValue("timestamp", api_stripSpace(asctime(localtime(&t))), pMemFrac);


	if( _pXml!= NULL && realizeXmlFraction(_pXml, pMemFrac) == NULL) {
		return (char *)NULL;
	} else {
		if(realizeXmlFraction(&xml, pMemFrac) == NULL)
			return (char *)NULL;
	}

	char *pRet = NULL;
	if( _pXml==NULL) {
		pRet = xml.xml_string.phead;
	} else {
		pRet = _pXml->xml_string.phead;
	}

	freeXmlFraction(pMemFrac);

	return pRet;
}
static char *pXmltNode	= "\t\t\t<node id =\"@nid@\">@value@</node>\r\n";
static char *pXmltList	= "\t\t<attribute id=\"@aid@\">\r\n@node@\r\n\t\t</attribute>\r\n";
static char *pXmltNet	= "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n\
<netinfo>\r\n\
\t<meta>\r\n\
\t\t<hostname>@hostname@</hostname>\r\n\
\t\t<os>@os@</os>\r\n\
\t\t<timestamp>@timestamp@</timestamp>\r\n\
\t</meta>\r\n\
\t<content>\r\n\
@attr@\r\n\
\t</content>\r\n\
</netinfo>\r\n";

#define P_DELI		" :\t\r\n"

char * getNetXml(struct XML *_pXml) {
	struct XmlFraction *pNetFrac	= NULL;
	struct XmlFraction *pListFrac	= NULL;
	struct XmlFraction *pNodeFrac	= NULL;
	struct ListNode *pLNode		= NULL;
	struct XML xml;
	String str = SSTR_INIT;
	char buf[MAX_BUF_SIZE];
	char buf2[MAX_BUF_SIZE];
	char buf3[MAX_BUF_SIZE];
	int aid = 0, nid = 0;

	bzero(buf, MAX_BUF_SIZE);

	if(getMachineInfo(NET_LOAD, &str) != 0) {
		return (char *)NULL;
	}
	if((pNetFrac = buildXmlFraction(pXmltNet, NULL)) == NULL) {
		return (char *)NULL;
	}

	do{
		if(sstrGetLine(&str, buf, MAX_BUF_SIZE) == SSTR_EOL)
			break;
		if(strlen(buf) == 0)
			continue;
		pLNode = textToList(buf, P_DELI);
		if(pLNode == NULL)
			return (char *)NULL;

		pListFrac = buildXmlFraction(pXmltList, NULL);
		if(pListFrac == NULL) 
			return (char *)NULL;
		++aid;
		snprintf(buf2,MAX_BUF_SIZE, "%d", aid);
		setXmlValue("aid", buf2, pListFrac);

		for(; pLNode != NULL; pLNode = pLNode->pnext) {
			pNodeFrac = buildXmlFraction(pXmltNode, NULL);
			if(pNodeFrac == NULL)
				return (char *)NULL;

			++nid;	
			snprintf(buf3, MAX_BUF_SIZE, "%d", nid);
			setXmlValue("nid", buf3, pNodeFrac);
			setXmlValue("value", pLNode->avalue, pNodeFrac);
			if(realizeXmlFraction(&xml, pNodeFrac) == NULL) {
				freeXmlFraction(pNodeFrac);
				freeXmlFraction(pListFrac);
				freeListNode(pLNode);
				return (char *)NULL;
			}

			setXmlValue("node", xml.xml_string.phead, pListFrac);
			freeXmlFraction(pNodeFrac);
		}

		
		if(realizeXmlFraction(&xml, pListFrac) == NULL) {
			freeXmlFraction(pListFrac);
			freeListNode(pLNode);
			return (char *)NULL;
		}
		setXmlValue("attr", xml.xml_string.phead, pNetFrac);

		freeXmlFraction(pListFrac);
		freeListNode(pLNode);
		nid = 0;


	} while(1);


	

	sstrClear(&str);
	if(getMachineInfo(KNL_HOST, &str) != 0) {
		return (char *)NULL;
	}
	api_stripSpace(str.phead);
	setXmlValue("hostname", str.phead, pNetFrac);

	sstrClear(&str);
	if(getMachineInfo(KNL_VERS, &str) != 0) {
		return (char *)NULL;
	}	
	api_stripSpace(str.phead);
	setXmlValue("os", str.phead, pNetFrac);
	time_t t=time(0);
	setXmlValue("timestamp", api_stripSpace(asctime(localtime(&t))), pNetFrac);


	if( _pXml!= NULL && realizeXmlFraction(_pXml, pNetFrac) == NULL) {
		return (char *)NULL;
	} else {
		if(realizeXmlFraction(&xml, pNetFrac) == NULL)
			return (char *)NULL;
	}

	char *pRet = NULL;
	if( _pXml==NULL) {
		pRet = xml.xml_string.phead;
	} else {
		pRet = _pXml->xml_string.phead;
	}

	freeXmlFraction(pNetFrac);

	return pRet;
}


static char *pXmltStat	= "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n\
<statinfo>\r\n\
\t<meta>\r\n\
\t\t<hostname>@hostname@</hostname>\r\n\
\t\t<os>@os@</os>\r\n\
\t\t<timestamp>@timestamp@</timestamp>\r\n\
\t</meta>\r\n\
\t<content>\r\n\
@attr@\r\n\
\t</content>\r\n\
</statinfo>\r\n";


char * getStatXml(struct XML *_pXml) {
	struct XmlFraction *pStatFrac	= NULL;
	struct XmlFraction *pListFrac	= NULL;
	struct XmlFraction *pNodeFrac	= NULL;
	struct ListNode *pLNode		= NULL;
	struct XML xml;
	String str = SSTR_INIT;
	char buf[MAX_BUF_SIZE];
	char buf2[MAX_BUF_SIZE];
	char buf3[MAX_BUF_SIZE];
	int aid = 0, nid = 0;

	bzero(buf, MAX_BUF_SIZE);

	if(getMachineInfo(SYS_LOAD, &str) != 0) {
		return (char *)NULL;
	}
	if((pStatFrac = buildXmlFraction(pXmltStat, NULL)) == NULL) {
		return (char *)NULL;
	}

	do{
		if(sstrGetLine(&str, buf, MAX_BUF_SIZE) == SSTR_EOL)
			break;
		if(strlen(buf) == 0)
			continue;
		pLNode = textToList(buf, P_DELI);
		if(pLNode == NULL)
			return (char *)NULL;

		pListFrac = buildXmlFraction(pXmltList, NULL);
		if(pListFrac == NULL) 
			return (char *)NULL;
		++aid;
		snprintf(buf2,MAX_BUF_SIZE, "%d", aid);
		setXmlValue("aid", buf2, pListFrac);

		for(; pLNode != NULL; pLNode = pLNode->pnext) {
			pNodeFrac = buildXmlFraction(pXmltNode, NULL);
			if(pNodeFrac == NULL)
				return (char *)NULL;

			++nid;	
			snprintf(buf3, MAX_BUF_SIZE, "%d", nid);
			setXmlValue("nid", buf3, pNodeFrac);
			setXmlValue("value", pLNode->avalue, pNodeFrac);
			if(realizeXmlFraction(&xml, pNodeFrac) == NULL) {
				freeXmlFraction(pNodeFrac);
				freeXmlFraction(pListFrac);
				freeListNode(pLNode);
				return (char *)NULL;
			}

			setXmlValue("node", xml.xml_string.phead, pListFrac);
			freeXmlFraction(pNodeFrac);
		}

		
		if(realizeXmlFraction(&xml, pListFrac) == NULL) {
			freeXmlFraction(pListFrac);
			freeListNode(pLNode);
			return (char *)NULL;
		}
		setXmlValue("attr", xml.xml_string.phead, pStatFrac);

		freeXmlFraction(pListFrac);
		freeListNode(pLNode);
		nid = 0;


	} while(1);


	

	sstrClear(&str);
	if(getMachineInfo(KNL_HOST, &str) != 0) {
		return (char *)NULL;
	}
	api_stripSpace(str.phead);
	setXmlValue("hostname", str.phead, pStatFrac);

	sstrClear(&str);
	if(getMachineInfo(KNL_VERS, &str) != 0) {
		return (char *)NULL;
	}	
	api_stripSpace(str.phead);
	setXmlValue("os", str.phead, pStatFrac);
	time_t t=time(0);
	setXmlValue("timestamp", api_stripSpace(asctime(localtime(&t))), pStatFrac);


	if( _pXml!= NULL && realizeXmlFraction(_pXml, pStatFrac) == NULL) {
		return (char *)NULL;
	} else {
		if(realizeXmlFraction(&xml, pStatFrac) == NULL)
			return (char *)NULL;
	}

	char *pRet = NULL;
	if( _pXml==NULL) {
		pRet = xml.xml_string.phead;
	} else {
		pRet = _pXml->xml_string.phead;
	}

	freeXmlFraction(pStatFrac);

	return pRet;
}


#ifdef __cplusplus
}
#endif


#endif /* End MONITOR_H_ */
